import debounce from "lodash.debounce";
import { event as gaEvent } from "nextjs-google-analytics";
import { toast } from "react-hot-toast";
import { create } from "zustand";
import { FileFormat } from "../enums/file.enum";
import useGraph from "../features/editor/views/GraphView/stores/useGraph";
import { isIframe } from "../lib/utils/helpers";
import { contentToJson, jsonToContent } from "../lib/utils/jsonAdapter";
import useConfig from "./useConfig";
import useJson from "./useJson";

const API_BASE = process.env.REACT_APP_API_BASE || "http://localhost/dev-api";

const initialStates = {
  fileData: null as File | null,
  format: FileFormat.JSON,
  contents: "",
  error: null as any,
  hasChanges: false,
  jsonSchema: null as object | null,
};

type FileStates = typeof initialStates;
type SetContents = {
  contents?: string;
  hasChanges?: boolean;
  skipUpdate?: boolean;
  format?: FileFormat;
};

type Query = string | string[] | undefined;

interface JsonActions {
  getContents: () => string;
  getFormat: () => FileFormat;
  getHasChanges: () => boolean;
  setError: (error: string | null) => void;
  setHasChanges: (hasChanges: boolean) => void;
  setContents: (data: SetContents) => void;
  fetchUrl: (url: string) => void;
  setFormat: (format: FileFormat) => void;
  clear: () => void;
  setFile: (fileData: File) => void;
  setJsonSchema: (jsonSchema: object | null) => void;
  checkEditorSession: (url: Query, widget?: boolean) => Promise<void>;
  fetchDefaultData: (search?: string) => Promise<void>;
}

export type File = {
  id: string;
  views: number;
  owner_email: string;
  name: string;
  content: string;
  private: boolean;
  format: FileFormat;
  created_at: string;
  updated_at: string;
};

const isURL = (value: string) => {
  return /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi.test(
    value
  );
};

const debouncedUpdateJson = debounce((value: unknown) => {
  useGraph.getState().setLoading(true);
  useJson.getState().setJson(JSON.stringify(value, null, 2));
}, 800);

const useFile = create<FileStates & JsonActions>()((set, get) => ({
  ...initialStates,

  clear: () => {
    set({ contents: "" });
    useJson.getState().clear();
  },

  setJsonSchema: jsonSchema => set({ jsonSchema }),

  setFile: fileData => {
    set({ fileData, format: fileData.format || FileFormat.JSON });
    get().setContents({ contents: fileData.content, hasChanges: false });
    gaEvent("set_content", { label: fileData.format });
  },

  getContents: () => get().contents,
  getFormat: () => get().format,
  getHasChanges: () => get().hasChanges,

  setFormat: async format => {
    try {
      const prevFormat = get().format;
      set({ format });

      const contentJson = await contentToJson(get().contents, prevFormat);
      const jsonContent = await jsonToContent(JSON.stringify(contentJson, null, 2), format);

      get().setContents({ contents: jsonContent });
    } catch (error) {
      get().clear();
      console.warn("The content was unable to be converted, so it was cleared instead.");
    }
  },

  setContents: async ({ contents, hasChanges = true, skipUpdate = false, format }) => {
    try {
      set({
        ...(contents && { contents }),
        error: null,
        hasChanges,
        format: format ?? get().format,
      });

      const isFetchURL = window.location.href.includes("?");
      const json = await contentToJson(get().contents, get().format);

      if (!useConfig.getState().liveTransformEnabled && skipUpdate) return;

      if (get().hasChanges && contents && contents.length < 80_000 && !isIframe() && !isFetchURL) {
        sessionStorage.setItem("content", contents);
        sessionStorage.setItem("format", get().format);
        set({ hasChanges: true });
      }

      debouncedUpdateJson(json);
    } catch (error: any) {
      if (error?.mark?.snippet) return set({ error: error.mark.snippet });
      if (error?.message) set({ error: error.message });
      useJson.setState({ loading: false });
      useGraph.setState({ loading: false });
    }
  },

  setError: error => set({ error }),
  setHasChanges: hasChanges => set({ hasChanges }),

  fetchUrl: async url => {
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
      
      const json = await res.json();
      const jsonStr = JSON.stringify(json, null, 2);

      get().setContents({ contents: jsonStr });
      useJson.setState({ json: jsonStr, loading: false });
    } catch (error) {
      get().clear();
      toast.error("Failed to fetch document from URL!");
    }
  },

  fetchDefaultData: async (search = "") => {
    try {
      const params = new URLSearchParams();
      if (search) params.set("search", search);
  
      const response = await fetch(`${API_BASE}/asset/purchase/get_saling_asset1?${params}`);
      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.msg || errorData.message || '请求失败');
      }
  
      const data = await response.json();
      const contents = JSON.stringify(data, null, 2);
  
      set({ contents, error: null });
      
      // 修复1：确保传递字符串给图表
      try {
        useGraph.getState().setGraph(contents); // 传递字符串而非对象
      } catch (error) {
        console.error('图表更新失败:', error);
        useGraph.getState().clearGraph();
      }

      gaEvent("fetch_default_data", { search_term: search });
  
    } catch (error) {
      console.error('Error fetching default data:', error);
      set({ 
        contents: JSON.stringify({ error }, null, 2),
        error: error
      });
      useGraph.getState().clearGraph();
      toast.error(`获取数据失败: ${error}`);
    }
  },

  checkEditorSession: async (url, widget) => {
    if (url && typeof url === "string" && isURL(url)) {
      return get().fetchUrl(url);
    }

    let contents = '';
    const sessionContent = sessionStorage.getItem("content");
    const format = sessionStorage.getItem("format") as FileFormat | null;
    
    if (sessionContent && !widget) {
      contents = sessionContent;
    } else {
      await get().fetchDefaultData();
      contents = get().contents;
    }

    if (format) set({ format });
    get().setContents({ contents, hasChanges: false });
  },
}));

export default useFile;
